﻿// -----------------------------------------------------------------------
// <copyright file="Utilities.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace ExcelMultipleFileFixer
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Runtime.InteropServices.ComTypes;
    using System.Text;

    using Microsoft.Win32;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public static class Utilities
    {
        [DllImport("ole32.dll", PreserveSig = false)]
        private static extern void CreateBindCtx(uint reserved, out IBindCtx ppbc);

        public static void OpenWithExcel(string filename)
        {
            bool throwaway;
            OpenWithExcel(filename, out throwaway);
        }

        public static IEnumerable<TOut> TrySelect<TIn, TOut>(this IEnumerable<TIn> enumerable, Func<TIn, TOut> selector)
        {
            foreach (var elt in enumerable)
            {
                TOut oOut;
                try
                {
                    oOut = selector(elt);
                }
                catch
                {
                    continue;
                }

                yield return oOut;
            }
        }

        public static void OpenWithExcel(string filename, out bool alreadyOpened)
        {
            if (!File.Exists(filename))
            {
                throw new FileNotFoundException("Could not find file specified", filename);
            }

            var file = new FileInfo(filename);

            var filesWithSameBasename =
                GetFilesOpenWithExcel().TrySelect(fn => new FileInfo(fn)).Where(fi => fi.Name == file.Name).ToList();

            alreadyOpened = filesWithSameBasename.Any(fi => fi.FullName == file.FullName);
            if (alreadyOpened)
            {
                TryActivateOpenExcelInstance(file.FullName);
                return;
            }

            if (filesWithSameBasename.Any() || !TryOpenWithExistingExcel(file.FullName))
            { // If I haven't been able to open it with an existing excel instance
                string excelExeLoc = GetMostRecentExcelLocation();
                Process.Start(excelExeLoc, PrepareFilePathForArg(file.FullName));
            }
        }

        private static string PrepareFilePathForArg(string filename)
        {
            return "\"" + filename.Replace("\"", "\\\"") + "\"";
        }

        public static bool TryActivateOpenExcelInstance(string filename)
        {
            return (bool)ExcelVbHelper.Helpers.TryActivateOpenExcelInstance(filename);
        }

        public static bool TryOpenWithExistingExcel(string filename)
        {
            return (bool)ExcelVbHelper.Helpers.OpenFileInExistingExcel(filename);
        }

        public static string GetMostRecentExcelLocation()
        {
            const string ExcelRegPath = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\excel.exe";
            var exeLocation = Registry.GetValue(ExcelRegPath, "Path", null) as string;
            return exeLocation + "EXCEL.EXE";
        }

        public static bool TryGetPropertyValue(object target, string propertyName, out object value)
        {
            var matchingProperties = target.GetType().GetProperties().Where(pi => pi.Name == propertyName).ToArray();
            if (!matchingProperties.Any())
            {
                value = null;
                return false;
            }

            value = target.GetType().InvokeMember(propertyName, System.Reflection.BindingFlags.GetProperty, null, target, null);
            var property = matchingProperties[0];
            value = property.GetValue(target, null);
            return true;
        }

        public static string[] GetFilesOpenWithExcel()
        {
            return GetRotKeys().ToArray();
        }

        public static string JoinStrings(this string delimiter, IEnumerable<string> toJoin)
        {
            var b = new StringBuilder();
            var enumerator = toJoin.GetEnumerator();
            if (!enumerator.MoveNext())
            {
                return string.Empty;
            }

            string prev = enumerator.Current;
            while (enumerator.MoveNext())
            {
                b.Append(prev);
                b.Append(delimiter);
                prev = enumerator.Current;
            }

            b.Append(prev);

            return b.ToString();
        }

        public static IEnumerable<string> GetRotKeys()
        {
            // http://blogs.msdn.com/b/kirillosenkov/archive/2011/08/10/how-to-get-dte-from-visual-studio-process-id.aspx
            IBindCtx ctx = null;
            IRunningObjectTable table = null;
            IEnumMoniker mon = null;
            var lst = new IMoniker[1];
            try
            {
                CreateBindCtx(0, out ctx);
                ctx.GetRunningObjectTable(out table);
                table.EnumRunning(out mon);
                var rotEntries = new List<string>();
                while (mon.Next(1, lst, IntPtr.Zero) == 0)
                {
                    IMoniker t = lst[0];
                    string runningObjectName;
                    ////object runningObjectVal;
                    try
                    {
                        t.GetDisplayName(ctx, null, out runningObjectName);

                        ////table.GetObject(t, out runningObjectVal);
                    }
                    catch
                    {
                        continue;
                    }

                    rotEntries.Add(runningObjectName);
                }

                return rotEntries;
            }
            finally
            {
                if (mon != null)
                {
                    Marshal.ReleaseComObject(mon);
                }
 
                if (table != null)
                {
                    Marshal.ReleaseComObject(table);
                }
 
                if (ctx != null)
                {
                    Marshal.ReleaseComObject(ctx);
                }
            }
        }
    }
}
